package com.geline.mybatisplus.handler;

import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.baomidou.mybatisplus.annotation.TableName;
import com.geline.cloud.util.DynamicSqlUtil;
import com.geline.mybatisplus.annotation.ExtractCount;
import com.geline.mybatisplus.annotation.ExtractKey;
import com.geline.mybatisplus.annotation.ExtractSum;
import com.geline.mybatisplus.context.ExtractTableHelper;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.parsing.XNode;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.cglib.beans.BeanMap;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;

import java.lang.reflect.Field;
import java.util.*;

/**
 * @author mx
 * @date 2025/9/20
 */
@Slf4j
public class ExtractTableHandler {
    private JdbcTemplate jdbcTemplate;

    public ExtractTableHandler(JdbcTemplate jdbcTemplate){
        this.jdbcTemplate = jdbcTemplate;
    }

    public JdbcTemplate getJdbcTemplate() {
        return jdbcTemplate;
    }

    public Map<String, Object> handleEntity(Object entity, String sourceTable){
        Map<String, Object> updateMap = new HashMap<>();
        BeanMap beanMap = BeanMap.create(entity);

        TableName annotation = entity.getClass().getAnnotation(TableName.class);
        String foreignKey = ExtractTableHelper.getForeignKey(annotation.value(), sourceTable);
        //设置值: @ExtractCount, @ExtractSum
        Map<String, String> countAndSumMap = handleExtractCountAndSum(entity, foreignKey);
        String dynamicSql = countAndSumMap.get(sourceTable);
        if(dynamicSql!=null){
            Object newEntity = selectEntity(dynamicSql, beanMap, entity.getClass());
            Map<String, Object> newMap = BeanMap.create(newEntity);
            for (String key : newMap.keySet()){
                Object value = newMap.get(key);
                if(value!=null){
                    beanMap.put(key, value);
                    updateMap.put(key, value);
                }
            }
        }

        //设置值: @ExtractKey
        List<ExtractKeyResult> list = handleExtractKey(entity, sourceTable);
        //排序：从小到大执行
        list.sort(Comparator.comparingInt(ExtractKeyResult::getOrder));
        for (ExtractKeyResult result : list){
            String selectSql = DynamicSqlUtil.parseSQL(result.getRunSql(), beanMap);
            if(selectSql.contains("#{") || selectSql.contains("${")){
                throw new RuntimeException("error selectSql : "+selectSql);
            }
            log.debug("selectSql  >>> {}", selectSql);
            String preSql = selectSql.substring(0, selectSql.toLowerCase().indexOf("from"));
            if(preSql.contains(",")){
                //查询多个字段
                Object object = jdbcTemplate.queryForObject(selectSql, new BeanPropertyRowMapper<>(result.getEntityType()));
                if(object!=null){
                    Map<String, Object> objMap = BeanMap.create(object);
                    for(String key : objMap.keySet()){
                        Object value = objMap.get(key);
                        if(value!=null){
                            beanMap.put(key, value);
                            updateMap.put(key, value);
                        }
                    }
                }
            }else {
                //查询1个字段
                Object value = jdbcTemplate.queryForObject(selectSql, result.getFieldType());
                String fieldName = result.getFieldName();
                beanMap.put(fieldName, value);
                updateMap.put(fieldName, value);
            }
        }

        return updateMap;
    }

    // 处理 @ExtractKey
    public List<ExtractKeyResult> handleExtractKey(Object entity, String sourceTable){
        List<ExtractKeyResult> list = new ArrayList<>();
        Field[] fields = entity.getClass().getDeclaredFields();
        for(Field field : fields){
            ExtractKey[] extractKeys = field.getAnnotationsByType(ExtractKey.class);
            if(extractKeys!=null && extractKeys.length>0){
                Class<?> fieldType = field.getType();
                if(!ClassUtil.isSimpleValueType(fieldType)){
                    log.warn("unSupport update field:{}, type={}", field.getName(), fieldType);
                    continue;
                }
                for(ExtractKey extractKey : extractKeys){
                    String value = extractKey.value();
                    String sqlIdInMapper = extractKey.sqlIdInMapper();
                    if(StrUtil.isBlank(value) && StrUtil.isNotBlank(sqlIdInMapper)){
                        value = getSqlInMapper(sqlIdInMapper);
                    }
                    if(value.contains(sourceTable) || extractKey.fromTables().contains(sourceTable)){
                        ExtractKeyResult result = new ExtractKeyResult(entity.getClass(), field.getName(), fieldType, value, extractKey.order());
                        //只添加与子表相关的汇总字段
                        list.add(result);
                    }
                }
            }
        }
        return list;
    }

    // 处理 @ExtractCount, ExtractSum
    public Map<String, String> handleExtractCountAndSum(Object entity, String foreignKey){
        Field[] fields = entity.getClass().getDeclaredFields();
        Map<String, String> runSqlMap = new HashMap<>();
        for(Field field : fields){
            ExtractCount[] extractCounts = field.getAnnotationsByType(ExtractCount.class);
            if(extractCounts!=null && extractCounts.length>0){
                for(ExtractCount extractCount : extractCounts){
                    String table = extractCount.table();
                    String value = runSqlMap.get(table);
                    if(value==null){
                        runSqlMap.put(table, "count(1) as "+field.getName());
                    }else {
                        runSqlMap.put(table, value + ", count(1) as "+field.getName());
                    }
                }
            }
            ExtractSum[] extractSums = field.getAnnotationsByType(ExtractSum.class);
            if(extractSums!=null && extractSums.length>0){
                for (ExtractSum extractSum : extractSums){
                    String table = extractSum.table();
                    String column = extractSum.column();
                    String value = runSqlMap.get(table);
                    if(value==null){
                        runSqlMap.put(table, String.format("ifnull(sum(%s),0) as %s", column, field.getName()));
                    }else {
                        runSqlMap.put(table, value + String.format(", ifnull(sum(%s),0) as %s", column, field.getName()));
                    }
                }
            }
        }
        for(String table : runSqlMap.keySet()){
            String columns = runSqlMap.get(table);
            if(foreignKey.contains(",")){
                StringBuffer sb = new StringBuffer();
                String[] keys = foreignKey.split(",");
                for (String key : keys){
                    sb.append(String.format(" and %s=#{%s}", StrUtil.toUnderlineCase(key), StrUtil.toCamelCase(key)));
                }
                String runSql = String.format("select %s from %s where 1=1%s", columns, table, sb.toString());
                runSqlMap.put(table, runSql);
            }else {
                String runSql = String.format("select %s from %s where %s=#{id}", columns, table, foreignKey);
                runSqlMap.put(table, runSql);
            }
        }
        log.debug("find @ExtractCount, @ExtractSum runSqlMap={}", runSqlMap);
        return runSqlMap;
    }

    public String getSqlInMapper(String sqlIdInMapper) {
        SqlSessionFactory sqlSessionFactory = SpringUtil.getBean(SqlSessionFactory.class);
        Configuration configuration = sqlSessionFactory.getConfiguration();
        XNode value = configuration.getSqlFragments().get(sqlIdInMapper);
        return value.getStringBody();
    }

    private <T> T selectEntity(String dynamicSql, Map<String, Object> example, Class<T> entityClass){
        String sql = DynamicSqlUtil.parseSQL(dynamicSql, example);
        //存在变量时不执行查询
        if(sql.contains("#{") || sql.contains("${")){
            log.warn("can not execute dynamicSql = {}", sql);
            log.debug("dynamicSql example = {}", example);
            return null;
        }
        try {
            return jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<>(entityClass));
        }catch (DataAccessException e){
            log.warn("queryForObject = {}", sql);
            log.warn(e.getMessage());
        }
        return null;
    }

    @Getter
    @Setter
    @AllArgsConstructor
    class ExtractKeyResult{
        private Class entityType;
        private String fieldName;
        private Class fieldType;
        private String runSql;
        private int order;
    }
}
